package com.lumlord.common.shiro.config;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SessionStorageEvaluator;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.HandlerExceptionResolver;

import com.lumlord.common.shiro.service.CustomCacheManager;
import com.lumlord.common.shiro.service.DbShiroRealm;
import com.lumlord.common.shiro.service.JWTShiroRealm;
import com.lumlord.common.spring.PubExceptionHandler;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import cn.hutool.core.codec.Base64;

/**
 * 权限配置文件
 * 
 * Shiro主要做3件事情， 1）用户登录时做用户名密码校验；<br/>
 * 2）用户登录后收到请求时做JWT Token的校验；<br/>
 * 3）用户权限的校验 <br/>
 * 
 * @ClassName: ShiroConfiguration
 * @author gpf
 * @date 2018年8月25日
 */
@Configuration
public class ShiroConfig {
	@Autowired
	private AnyRolesAuthorizationFilter anyRolesAuthorizationFilter;
	@Autowired
	private JwtAuthFilter jwtAuthFilter;
	@Autowired
	private DbShiroRealm dbShiroRealm;
	@Autowired
	private JWTShiroRealm jwtShiroRealm;

	/**
	 * redis 缓存
	 */
	@Autowired
	private CustomCacheManager redisCacheManager;

	@Bean
	public FilterRegistrationBean<Filter> delegatingFilterProxy() {
		FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<Filter>();
		DelegatingFilterProxy proxy = new DelegatingFilterProxy();
		proxy.setTargetBeanName("shiroFilter");
		filterRegistrationBean.setFilter(proxy);

		// 链式执行
		filterRegistrationBean.addInitParameter("targetFilterLifecycle", "true");

		filterRegistrationBean.setAsyncSupported(true);
		filterRegistrationBean.setEnabled(true);
		proxy.setTargetFilterLifecycle(true);
		filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST);

		return filterRegistrationBean;
	}

	/**
	 * 这是shiro的大管家，相当于mybatis里的SqlSessionFactoryBean
	 * 
	 * @param securityManager
	 * @return
	 */
	@Bean("shiroFilter")
	public ShiroFilterFactoryBean shiroFilterFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		Map<String, Filter> filterMap = new HashMap<>();
		filterMap.put("authcToken", jwtAuthFilter);
		filterMap.put("anyRole", anyRolesAuthorizationFilter);

		loginInput(shiroFilterFactoryBean);

		shiroFilterFactoryBean.setFilters(filterMap);
		shiroFilterFactoryBean.setFilterChainDefinitionMap(ShiroFilterMapFactory.shiroFilterMap());
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		
		return shiroFilterFactoryBean;
	}

	/**
	 * 登录相关
	 * 
	 * @param shiroFilterFactoryBean
	 */
	private void loginInput(ShiroFilterFactoryBean shiroFilterFactoryBean) {
		shiroFilterFactoryBean.setLoginUrl("/api/custom/v1/user/login");
		shiroFilterFactoryBean.setSuccessUrl("/api/custom/v1/user/input");
	 
	}

	/**
	 * 禁用session, 不保存用户登录状态。保证每次请求都重新认证。
	 * 需要注意的是，如果用户代码里调用Subject.getSession()还是可以用session，如果要完全禁用，要配合下面的noSessionCreation的Filter来实现
	 */
	@Bean
	protected SessionStorageEvaluator sessionStorageEvaluator() {
		DefaultWebSessionStorageEvaluator sessionStorageEvaluator = new DefaultWebSessionStorageEvaluator();
		sessionStorageEvaluator.setSessionStorageEnabled(false);
		sessionStorageEvaluator.setSessionStorageEnabled(true);
		return sessionStorageEvaluator;
	}

	/**
	 * web应用管理配置
	 * 
	 * @param shiroRealm
	  * @param manager
	 * @return
	 */
	@Bean
	public DefaultWebSecurityManager securityManager(Realm shiroRealm, RememberMeManager manager) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		 securityManager.setCacheManager(redisCacheManager);
		securityManager.setRememberMeManager(manager);// 记住Cooki
		securityManager.setRealm(shiroRealm);
		 
		return securityManager;
	}

	/**
	 * 加密算法
	 * 
	 * @return
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 采用MD5 进行加密
		hashedCredentialsMatcher.setHashIterations(1);// 加密次数
		return hashedCredentialsMatcher;
	}

	/**
	 * 记住我的配置
	 * 
	 * @return
	 */
	@Bean
	public RememberMeManager rememberMeManager() {
		Cookie cookie = new SimpleCookie("rememberMe");
		cookie.setHttpOnly(true);// 通过js脚本将无法读取到cookie信息
		cookie.setMaxAge(60 * 60 * 24);// cookie保存一天
		CookieRememberMeManager manager = new CookieRememberMeManager();
		manager.setCookie(cookie);
		// 手动设置对称加密秘钥，防止重启系统后系统生成新的随机秘钥，防止导致客户端cookie无效
		manager.setCipherKey(Base64.decode("6ZmI6I2j3Y+R1aSn5BOlAA=="));
		return manager;
	}

	 
	/**
	 * 配置realm，用于认证和授权
	 * 
	 * @param hashedCredentialsMatcher
	 * @return
	 */
	@Bean("shiroRealm")
	public AuthorizingRealm shiroRealm(HashedCredentialsMatcher hashedCredentialsMatcher) {

		hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
		// 校验密码用到的算法
		dbShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
		dbShiroRealm.setCacheManager(redisCacheManager);
		return dbShiroRealm;
	}

	@Bean("jwtShiroRealm")
	public AuthorizingRealm jwtShiroRealm(HashedCredentialsMatcher hashedCredentialsMatcher) {

		hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
		// 校验密码用到的算法
		jwtShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
		jwtShiroRealm.setCacheManager(redisCacheManager);
		return jwtShiroRealm;
	}

	/**
	 * 启用shiro方言，这样能在页面上使用shiro标签
	 * 
	 * @return
	 */
	@Bean
	public ShiroDialect shiroDialect() {
		return new ShiroDialect();
	}

	/**
	 * 启用shiro注解 加入注解的使用，不加入这个注解不生效
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(
			org.apache.shiro.mgt.SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
		advisor.setSecurityManager(securityManager);
		return advisor;
	}

	/**
	 * 注册全局异常处理
	 * 
	 * @return
	 */
	@Bean(name = "exceptionHandler")
	public HandlerExceptionResolver handlerExceptionResolver() {
		return new PubExceptionHandler();
	}
	
	
 
}
